/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.support.scripting.parser;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.EntityNotFoundException;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.entities.beans.LogicBean;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.support.scripting.ScriptParser;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.util.EntityUtils;

import static cn.easyplatform.type.Constants.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public abstract class AbstractScriptParser implements ScriptParser {

    boolean beginQuotes = false;
    boolean beginDbQuotes = false;

    @Override
    public String parse(CommandContext cc, RecordContext rc, String expr) {
        return parse(cc, rc, expr, false).trim();
    }

    private String parse(CommandContext cc, RecordContext rc, String expr, boolean isSetValue) {
        char[] cs = expr.toCharArray();
        int len = cs.length;
        StringBuilder sb = new StringBuilder(len * 3);
        sb.append(" ");
        StringBuilder tmp = new StringBuilder();
        for (int i = 0; i < len; i++) {
            if (cs[i] == '"') {
                if (i > 1 && cs[i - 1] == '\\' && cs[i - 2] != '\\') {// 转义符不处理
                } else
                    beginDbQuotes = !beginDbQuotes;
            }
            if (!beginDbQuotes && cs[i] == '\'') {
                if (i > 1 && cs[i - 1] == '\\' && cs[i - 2] != '\\') {// 转义符不处理
                } else
                    beginQuotes = !beginQuotes;
            }
            if (!beginQuotes && !beginDbQuotes) {
                if (i < len - 1) {
                    if (cs[i] == '-' && cs[i + 1] == '>') {// 处理->
                        sb.append("$");
                        i++;
                    } else if (cs[i] == '/' && cs[i + 1] == '/') {// 处理注解符号//
                        while (i < len) {
                            sb.append(cs[i]);
                            if (cs[i] == '\n') {
                                break;
                            }
                            i++;
                        }
                    } else if (cs[i] == '/' && cs[i + 1] == '*') {// 处理注解符号/*和*/
                        while (i < len) {
                            sb.append(cs[i]);
                            if (cs[i] == '*' && i < len - 1 && cs[i + 1] == '/') {
                                break;
                            }
                            i++;
                        }
                    } else if (cs[i] == '&' && Character.isLetter(cs[i + 1])) {// &保留字,&now表示当前时间
                        i++;
                        tmp.setLength(0);
                        while (i < len) {
                            if (!Character.isLetter(cs[i])) {
                                i--;
                                break;
                            }
                            tmp.append(cs[i]);
                            i++;
                        }
                        int size = sb.length() - 1;
                        if (sb.charAt(size) == '$')
                            sb.deleteCharAt(size);
                        else
                            sb.append("$");
                        sb.append(".getReserveWord('").append(tmp).append("')");
                    } else if ((cs[i] == '$' || cs[i] == '@')) {// 处理$和@变量标识符
                        if (Character.isLetterOrDigit(cs[i + 1])
                                || cs[i + 1] == '_' || cs[i + 1] == '$'
                                || cs[i + 1] == '@') {
                            char c = cs[i];
                            char m = ' ';
                            tmp.setLength(0);
                            Character bk = null;
                            i++;
                            if (cs[i] == '$' || cs[i] == '@') {
                                m = cs[i];
                                i++;
                            }
                            while (i < len) {
                                if (!Character.isLetterOrDigit(cs[i])
                                        && cs[i] != '.' && cs[i] != '_') {// 变量可以字符、数字、下划线组成，并且以.作为操作符
                                    bk = cs[i];
                                    break;
                                } else
                                    tmp.append(cs[i]);
                                i++;
                            }
                            String name = tmp.toString();
                            String type = null;
                            boolean hasMethod = false;
                            int pos = name.indexOf(".");
                            if (pos > 0) {
                                type = name.substring(pos + 1);
                                if (i < len && cs[i] == '(')
                                    hasMethod = true;
                                name = name.substring(0, pos);
                            }
                            tmp.setLength(0);
                            boolean isEquals = false;
                            int index = i;
                            char op = ' ';
                            boolean qflag = false;
                            boolean dqflag = false;
                            int brackets = 0;
                            while (index < len) {
                                if (cs[index] == '\'' && !dqflag)
                                    qflag = !qflag;
                                if (cs[index] == '"')
                                    dqflag = !dqflag;
                                if (!qflag && !dqflag) {
                                    if (cs[index] == '(')
                                        brackets++;
                                    else if (cs[index] == ')') {
                                        brackets--;
                                        if (brackets < 0)
                                            break;
                                    } else if (cs[index] == ';'
                                            || cs[index] == '\n'
                                            || cs[index] == '{')
                                        break;
                                    if (!isEquals) {
                                        if (Character.isLetter(cs[index]))
                                            break;
                                        if ((cs[index] == '+'
                                                && index < len - 1 && cs[index + 1] == '+')
                                                || (cs[index] == '-'
                                                && index < len - 1 && cs[index + 1] == '-')) {// ++和--运算
                                            op = cs[index];
                                            tmp.append("1");
                                            isEquals = true;
                                            index++;
                                        } else if (cs[index] == '='
                                                && index < len - 1
                                                && cs[index + 1] != '='
                                                && cs[index - 1] != '='
                                                && cs[index - 1] != '!'
                                                && cs[index - 1] != '>'
                                                && cs[index - 1] != '<'
                                                && cs[index - 1] != ')') {// (+|-|*|/|%)=运算
                                            op = cs[index - 1];
                                            isEquals = true;
                                        }
                                    } else
                                        tmp.append(cs[index]);
                                } else if (isEquals)
                                    tmp.append(cs[index]);
                                index++;
                            }
                            int size = sb.length() - 1;
                            if (sb.charAt(size) == '$')
                                sb.deleteCharAt(size);
                            else
                                sb.append("$");
                            if (isEquals) {
                                String self = null;
                                if (op == '+' || op == '-' || op == '*'
                                        || op == '/' || op == '%') {// 处理$var*=$xx
                                    size = sb.length() - 1;
                                    StringBuilder strs = new StringBuilder();
                                    char[] tmps = sb.toString().toCharArray();
                                    for (int ii = size; ii >= 0; ii--) {
                                        if (!Character
                                                .isLetterOrDigit(tmps[ii])
                                                && tmps[ii] != '$'
                                                && tmps[ii] != '_') {
                                            break;
                                        } else
                                            strs.append(tmps[ii]);
                                    }
                                    strs.reverse();
                                    strs.append(".getVariable('").append(name)
                                            .append("','").append(c)
                                            .append("','')").append(op);
                                    self = strs.toString();
                                    strs = null;
                                }
                                sb.append(".setVariable('").append(name)
                                        .append("',");
                                if (self != null)
                                    sb.append(self);
                                sb.append(
                                        parse(cc, rc, tmp.toString(), true).trim())
                                        .append(",'").append(c).append("','");
                                if (type == null)
                                    sb.append(m == ' ' ? "" : m);
                                else if (!hasMethod)
                                    sb.append(type);
                                sb.append("')");
                                if (index < len)
                                    sb.append(cs[index]);
                                setMappingField(name);
                                i = index;
                            } else {
                                sb.append(".getVariable('").append(name)
                                        .append("','").append(c).append("','");
                                if (type == null)
                                    sb.append(m == ' ' ? "" : m);
                                else if (!hasMethod)
                                    sb.append(type);
                                // sb.append(type == null ? m == ' ' ? "" : m
                                // : type);
                                sb.append("')");
                                if (hasMethod)
                                    sb.append(".").append(type);
                                if (bk != null)
                                    sb.append(bk);
                                sb.append(tmp);
                            }
                        } else
                            sb.append(cs[i]);
                    } else if (cs[i] == '#') {
                        if (i < len - 6 && cs[i + 1] == 'u' && cs[i + 2] == 't'
                                && cs[i + 3] == 'i' && cs[i + 4] == 'l'
                                && cs[i + 5] == '.') {// 处理#util.method
                            i += 5;
                            tmp.setLength(0);
                            char c = ' ';
                            while (i < len) {
                                if (cs[i] == ';') {
                                    c = cs[i];
                                    break;
                                } else
                                    tmp.append(cs[i]);
                                i++;
                            }
                            int size = sb.length() - 1;
                            if (sb.charAt(size) == '$')
                                sb.deleteCharAt(size);
                            else
                                sb.append("$");
                            sb.append(".getUtilCmd()");
                            if (tmp.charAt(0) == '\'' || tmp.charAt(0) == '"')
                                sb.append(tmp);
                            else
                                sb.append(parse(cc, rc, tmp.toString(), false)
                                        .trim());
                            if (c != ' ')
                                sb.append(c);
                        } else if (i < len - 4 && cs[i + 3] == '.') {// 处理#db|dt.method
                            String name = cs[i + 1] + "" + cs[i + 2];
                            i += 3;
                            tmp.setLength(0);
                            char c = ' ';
                            while (i < len) {
                                if (cs[i] == ';') {
                                    c = cs[i];
                                    break;
                                } else
                                    tmp.append(cs[i]);
                                i++;
                            }
                            int size = sb.length() - 1;
                            if (sb.charAt(size) == '$')
                                sb.deleteCharAt(size);
                            else
                                sb.append("$");
                            if (name.equals("db"))
                                sb.append(".getDbCmd()");
                            else if (name.equals("dt"))
                                sb.append(".getDateCmd()");
                            else
                                throw new EasyPlatformWithLabelKeyException(
                                        "script.engine.cmd.no.create", name, "");
                            if (tmp.charAt(0) == '\'' || tmp.charAt(0) == '"')
                                sb.append(tmp);
                            else
                                sb.append(parse(cc, rc, tmp.toString(), false)
                                        .trim());
                            if (c != ' ')
                                sb.append(c);
                        } else if (i < len - 9 && cs[i + 1] == 'i'
                                && cs[i + 2] == 'n' && cs[i + 3] == 'c'
                                && cs[i + 4] == 'l' && cs[i + 5] == 'u'
                                && cs[i + 6] == 'd' && cs[i + 7] == 'e'
                                && (cs[i + 8] == ' ' || cs[i + 8] == '(')) {
                            // 处理#include "xyz";#include $var
                            i += 9;
                            tmp.setLength(0);
                            while (i < len) {
                                if (cs[i] == ';' || cs[i] == ')')
                                    break;
                                else //if (cs[i] != ' ')
                                    tmp.append(cs[i]);
                                i++;
                            }
                            if (cc != null) {
                                String id = tmp.toString().trim();
                                if (id.equals("super")) {
                                    WorkflowContext ctx = cc.getWorkflowContext();
                                    String name = ctx.getParameterAsString("808");
                                    String script = null;
                                    if (name.equals(ON_REFRESH) || name.equals(ON_OK)
                                            || name.equals(ON_LOAD) || name.equals(ON_BACK)) {// 页面
                                        script = EntityUtils.getSuperPageScript(cc,
                                                name, ctx.getParameterAsString("839"));
                                    } else {// task
                                        script = EntityUtils.getSuperTaskScript(cc,
                                                name, ctx.getParameterAsString("827"));
                                    }
                                    if (!Strings.isBlank(script))
                                        sb.append(parse(cc, rc, script, false).trim());
                                } else {
                                    if (id.startsWith("$")) {//
                                        id = (String) rc
                                                .getValue(id.substring(1));
                                    } else
                                        id = id.substring(1, id.length() - 1);
                                    if (id.indexOf("(") > 0 || id.indexOf(";") > 0
                                            || id.indexOf("=") > 0) {
                                        sb.append(parse(cc, rc, id, false)
                                                .trim());
                                    } else {
                                        LogicBean lb = cc.getEntity(id);
                                        if (lb == null)
                                            throw new EntityNotFoundException(
                                                    EntityType.LOGIC.getName(), id);
                                        sb.append(parse(cc, rc, lb.getContent(), false)
                                                .trim());
                                    }
                                }
                            }
                        } else if (i < len - 5 && cs[i + 1] == 'a'
                                && cs[i + 2] == 'p' && cs[i + 3] == 'p'
                                && cs[i + 4] == '.') {// 处理#app.method
                            i += 4;
                            tmp.setLength(0);
                            char c = ' ';
                            while (i < len) {
                                if (cs[i] == ';') {
                                    c = cs[i];
                                    break;
                                } else
                                    tmp.append(cs[i]);
                                i++;
                            }
                            int size = sb.length() - 1;
                            if (sb.charAt(size) == '$')
                                sb.deleteCharAt(size);
                            else
                                sb.append("$");
                            sb.append(".getAppCmd()");
                            if (tmp.charAt(0) == '\'' || tmp.charAt(0) == '"')
                                sb.append(tmp);
                            else
                                sb.append(parse(cc, rc, tmp.toString(), false)
                                        .trim());
                            if (c != ' ')
                                sb.append(c);
                        } else if (i < len - 5 && cs[i + 1] == 'b'
                                && cs[i + 2] == 'p' && cs[i + 3] == 'm'
                                && cs[i + 4] == '.') {// 处理#bpm.method
                            i += 4;
                            tmp.setLength(0);
                            char c = ' ';
                            while (i < len) {
                                if (cs[i] == ';') {
                                    c = cs[i];
                                    break;
                                } else
                                    tmp.append(cs[i]);
                                i++;
                            }
                            int size = sb.length() - 1;
                            if (sb.charAt(size) == '$')
                                sb.deleteCharAt(size);
                            else
                                sb.append("$");
                            sb.append(".getBpmCmd()");
                            if (tmp.charAt(0) == '\'' || tmp.charAt(0) == '"')
                                sb.append(tmp);
                            else
                                sb.append(parse(cc, rc, tmp.toString(), false)
                                        .trim());
                            if (c != ' ')
                                sb.append(";");
                        } else if (i < len - 8 && cs[i + 1] == 'i'
                                && cs[i + 2] == 'm' && cs[i + 3] == 'p'
                                && cs[i + 4] == 'o' && cs[i + 5] == 'r'
                                && cs[i + 6] == 't' && (cs[i + 7] == ' ')) {
                            //#import xy.xyz.abc
                            i += 8;
                            tmp.setLength(0);
                            char c = ' ';
                            while (i < len) {
                                if (cs[i] == ';')
                                    break;
                                else {//if (cs[i] != ' ') {
                                    tmp.append(cs[i]);
                                    if (cs[i] == '.')
                                        c = cs[i + 1];
                                }
                                i++;
                            }
                            if (Character.isUpperCase(c))
                                sb.append("importClass(Packages.").append(tmp).append(");");
                            else
                                sb.append("importPackage(Packages.").append(tmp)
                                        .append(");");
                        } else {// 处理其它自定义逻辑命令对象#xxx.method
                            tmp.setLength(0);
                            i++;
                            while (i < len) {
                                if (cs[i] == '.')
                                    break;
                                else
                                    tmp.append(cs[i]);
                                i++;
                            }
                            String name = tmp.toString().trim();
                            int size = sb.length() - 1;
                            if (sb.charAt(size) == '$')
                                sb.deleteCharAt(size);
                            else
                                sb.append("$");
                            sb.append(".getCustomCmd('").append(name)
                                    .append("')");
                            sb.append(cs[i]);
                        }
                    } else if (cs[i] == 'r' && i < len - 7 && cs[i + 1] == 'e'
                            && cs[i + 2] == 't' && cs[i + 3] == 'u'
                            && cs[i + 4] == 'r' && cs[i + 5] == 'n'
                            && (cs[i + 6] == ' ' || cs[i + 6] == '(')) {// 处理return
                        i += 7;
                        tmp.setLength(0);
                        while (i < len) {
                            if (cs[i] == ';')
                                break;
                            else //if (cs[i] != ' ')
                                tmp.append(cs[i]);
                            i++;
                        }
                        int size = sb.length() - 1;
                        if (sb.charAt(size) == '$')
                            sb.deleteCharAt(size);
                        else
                            sb.append("$");
                        sb.append(".exit(")
                                .append(parse(cc, rc, tmp.toString(), false).trim())
                                .append(");");
                    } else if (cs[i] == 'i' && i < len - 7 && cs[i + 1] == 'm'
                            && cs[i + 2] == 'p' && cs[i + 3] == 'o'
                            && cs[i + 4] == 'r' && cs[i + 5] == 't'
                            && cs[i + 6] == ' ') {// 处理import xxx.xx.xxxx;
                        i += 7;
                        tmp.setLength(0);
                        char c = ' ';
                        while (i < len) {
                            if (cs[i] == ';')
                                break;
                            else{// if (cs[i] != ' ') {
                                tmp.append(cs[i]);
                                if (cs[i] == '.')
                                    c = cs[i + 1];
                            }
                            i++;
                        }
                        if (Character.isUpperCase(c))
                            sb.append("importClass(Packages.").append(tmp).append(");");
                        else
                            sb.append("importPackage(Packages.").append(tmp)
                                    .append(");");
                    } else
                        sb.append(cs[i]);
                } else
                    sb.append(cs[i]);
            } else
                sb.append(cs[i]);
        }
        return sb.toString();
    }

    protected void setMappingField(String field) {
    }
}
